package com.ztgf.message.api.handler;

import com.alibaba.fastjson.JSON;
import com.ztgf.common.utils.StrUtils;
import com.ztgf.message.model.dto.RequestDTO;
import com.ztgf.message.model.dto.ResponseDTO;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import io.netty.util.AttributeMap;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @Description ：自定义的Handler
 * @Author ： mx.li
 * @Date ： 2018/10/10 10:16
 */
@Slf4j
@Component
public class NettyHandler extends ChannelInboundHandlerAdapter {

    private static final String URI = "websocket";
    private WebSocketServerHandshaker handshaker;
    private ChannelRepository channelRepository = ChannelRepository.getInstance();
    public static NettyHandler nettyHandler;


    @PostConstruct
    public void init() {
        nettyHandler = this;
    }

    /**
     * 处理消息
     * @param ctx
     * @param o
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object o) throws Exception {
//        log.info("【channelRead】=====>"+ctx.channel().id().toString()+"------------"+JSON.toJSONString(o));
        if (o instanceof FullHttpRequest) { // 传统的HTTP接入
            doHandleHttpRequest(ctx, (FullHttpRequest) o);
        } else if (o instanceof WebSocketFrame) { // WebSocket接入
            doHandlerWebSocketFrame(ctx, (WebSocketFrame) o);
        }
    }

    /**
     * wetsocket第一次连接握手
     * @param ctx
     * @param msg
     */
    private String doHandleHttpRequest(ChannelHandlerContext ctx, HttpRequest msg) {
        log.info("【doHandleHttpRequest】=====>"+ctx.channel().id().toString() + "----account:"+channelRepository.getClientIdByChanneId(ctx.channel().id().toString()));
        // http 解码失败
        if(!msg.decoderResult().isSuccess() || (!"websocket".equals(msg.headers().get("Upgrade")))){
            sendHttpResponse(ctx, (FullHttpRequest) msg, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
        }
        //可以获取msg的uri来判断
        String requestUrl = null;
        try {
            String uri = msg.uri();
            System.out.println(uri);
            //String uncodeParams = AESUtil.decrypt(requestParams);
            //System.out.println(uncodeParams);
            String clientId = uri.substring(9);//就是userId
            System.out.println("传入的clientId是：" + clientId);
            if(StrUtils.isEmpty(clientId)){
                closeChannel(ctx);
                return "";
            } else {
                Channel channel = channelRepository.getChannelByClientId(clientId);
                if(channel != null){
                    closeChannel(channel);
                }
                channelRepository.put(clientId, ctx.channel());
            }
        } catch (Exception e) {
            e.printStackTrace();
//            ctx.close();
            closeChannel(ctx);
            return "";
        }
        AttributeMap attributeMap = ctx;
        attributeMap.attr(AttributeKey.valueOf("type")).set(requestUrl);
        //可以通过url获取其他参数
        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(
                "ws://"+msg.headers().get("Host")+"/"+URI+"",null,false
        );
        handshaker = factory.newHandshaker(msg);
        if(handshaker == null){
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        }
        //进行连接
        handshaker.handshake(ctx.channel(), (FullHttpRequest) msg);
        //可以做其他处理
        return ctx.channel().id().toString();
    }

    /**
     * websocket消息处理
     * @param ctx
     * @param msg
     */
    private void doHandlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame msg) {
//        log.info("【doHandlerWebSocketFrame】=====>"+ctx.channel().id().toString());
        //判断msg 是哪一种类型  分别做出不同的反应
        if(msg instanceof CloseWebSocketFrame){
//            log.info("【关闭】");
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) msg);
            return ;
        }
        if(msg instanceof PingWebSocketFrame){
            log.info("【ping】");
            PongWebSocketFrame pong = new PongWebSocketFrame(msg.content().retain());
            ctx.channel().writeAndFlush(pong);
            return ;
        }
//        if(msg instanceof PongWebSocketFrame){
//           //log.info("【pong】" + ctx.channel().id().toString());
//            PingWebSocketFrame ping = new PingWebSocketFrame(msg.content().retain());
//            ctx.channel().writeAndFlush(ping);
//            ping.release();
//            return ;
//        }
        if(!(msg instanceof TextWebSocketFrame)){
//            log.info("【不支持二进制】");
            throw new UnsupportedOperationException("不支持二进制");
        }
        //可以对消息进行处理

        //群发
//        for (Channel channel : GlobalUserUtil.channels) {
//            channel.writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame) msg).text()));
//        }

        //获取发来的消息
        String text =((TextWebSocketFrame)msg).text();
        try{
            String uncodeText = text;//AESUtil.decrypt(text);
            Long one = System.currentTimeMillis();
            log.info("-----------uncodeText---------------"+uncodeText);
            RequestDTO dto = JSON.parseObject(uncodeText, RequestDTO.class);
//            ResponseDTO responseDTO = nettyHandler.messageService.doMessage(dto);
//            channelRepository.sendMessage(ctx.channel(), responseDTO);
//            Long two = System.currentTimeMillis();
            //log.info("-----------发送消息结束--------------" + (two - one));
        } catch (Exception e){
            log.error("参数错误", e);
            ResponseDTO responseDTO = new ResponseDTO();
            responseDTO.setCode(1);
            responseDTO.setMsg("参数错误");
            channelRepository.sendMessage(ctx.channel(), responseDTO);
        }
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
//        log.info("【sendHttpResponse】=====>"+ctx.channel().id().toString());
        // 返回应答给客户端
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }


    /**
     * 断开连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().toString();
        String clientId = channelRepository.getClientIdByChanneId(channelId);
//        if(StrUtils.isNotEmpty(clientId)){
//            Long userId = Long.parseLong(clientId);
//            log.info("--------断开连接------handlerRemoved----"+ ctx.channel().id() + ";用户id:" + userId);
//            Result<List<PkSingleDTO>> pkSingleResult = nettyHandler.learnFeign.pkSingleUserDropped(userId);
//            ResponseDTO responseDTO = new ResponseDTO();
//            if (pkSingleResult.getData() != null) {
//                List<PkSingleDTO> pkSingleDTOList = pkSingleResult.getData();
//                if (pkSingleDTOList.size() != 0) {
//                    pkSingleDTOList.forEach(pkSingleDTO -> {
//                        responseDTO.setCode(pkSingleResult.getCode());
//                        responseDTO.setMsg(pkSingleResult.getMsg());
//                        responseDTO.setParams("");
//                        responseDTO.setMethod("pkSingleUserOutWait");
//                        responseDTO.setMessageId(userId.toString());
//                        responseDTO.setData(pkSingleDTO);
//                        if (userId.equals(pkSingleDTO.getReceiveUserId()) && pkSingleDTO.getInviteUserInWait() == 1) {
//                                channelRepository.sendMessage(pkSingleDTO.getInviteUserId().toString(), responseDTO);
//                        }
//                        if (userId.equals(pkSingleDTO.getInviteUserId()) && pkSingleDTO.getReceiveUserInWait() == 1) {
//                                channelRepository.sendMessage(pkSingleDTO.getReceiveUserId().toString(), responseDTO);
//                        }
//                    });
//                }
//            }
//
//            Result<List<PkTeamVO>> pkTeamResult = nettyHandler.learnFeign.pkTeamUserDropped(userId);
//            if (pkTeamResult.getData() != null) {
//                List<PkTeamVO> pkTeamVOList = pkTeamResult.getData();
//                if (pkTeamVOList.size() != 0) {
//                    pkTeamVOList.forEach(pkTeamVO -> {
//                        List<PkTeamDeptVO> pkTeamDeptVOList = pkTeamVO.getPkTeamDeptVOList();
//                        for (PkTeamDeptVO pkTeamDeptVO : pkTeamDeptVOList) {
//                            for (PkTeamMemberDTO pkTeamMemberDTO : pkTeamDeptVO.getPkTeamMemberDTOList()) {
//                                if (!userId.equals(pkTeamMemberDTO.getUserId())) {
//                                    if (pkTeamMemberDTO.getUserInWait() == 1) {
//                                        responseDTO.setCode(pkTeamResult.getCode());
//                                        responseDTO.setMsg(pkTeamResult.getMsg());
//                                        responseDTO.setParams("");
//                                        responseDTO.setMethod("pkTeamUserOutWait");
//                                        responseDTO.setMessageId(userId.toString());
//                                        responseDTO.setData(pkTeamVO);
//                                        channelRepository.sendMessage(pkTeamMemberDTO.getUserId().toString(), responseDTO);
//                                    }
//                                }
//                            }
//                        }
//                    });
//                }
//            }
//        }
        channelRepository.remove(ctx.channel());
    }

    /**
     * 连接异常   需要关闭相关资源
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        log.error("【系统异常】======>"+cause.toString());
        closeChannel(ctx);
    }

    /**
     * 活跃的通道  也可以当作用户连接上客户端进行使用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        log.info("【channelActive】=====>"+ctx.channel().id().toString());
    }

    /**
     * 不活跃的通道  就说明用户失去连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        closeChannel(ctx);
    }

    /**
     * 这里只要完成 flush
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        log.info("【channelReadComplete】=====>"+ctx.channel().id().toString());
        ctx.flush();
    }

    /**
     * 这里是保持服务器与客户端长连接  进行心跳检测 避免连接断开
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//        log.info("【userEventTriggered】=====>"+ctx.channel().id().toString());
        if(evt instanceof IdleStateEvent){
            IdleStateEvent stateEvent = (IdleStateEvent) evt;
            if (stateEvent.state() == IdleState.READER_IDLE) {
//                log.info("【" + ctx.channel().remoteAddress() + "】读空闲超时（客服端）");
            } else if (stateEvent.state() == IdleState.WRITER_IDLE) {
//                log.info("【" + ctx.channel().remoteAddress() + "】写空闲超时（客服端）");
            } else if (stateEvent.state() == IdleState.ALL_IDLE) {
//                log.info("【"+ctx.channel().remoteAddress()+"】读写空闲超时 (客服端)");
            }
        }
    }

    private void closeChannel(ChannelHandlerContext ctx){
        try{
            ctx.channel().close();
            ctx.close();
        }catch (Exception e){
            log.error( "channel close is error",e);
        } finally {
            ctx.close();
        }
    }

    private void closeChannel(Channel channel){
        try{
            channelRepository.remove(channel);
            channel.close();
        }catch (Exception e){
            log.error( "channel close is error",e);
        } finally {
            channel.close();
        }
    }
}
